﻿using MQTTnet.Client;
using MQTTnet.Protocol;
using MQTTnet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UtilsMQ.Model;
using MQTTnet.Server;
using System.Diagnostics.CodeAnalysis;
using UtilsCache;
using Microsoft.EntityFrameworkCore;
using UtilsORM;
using UtilsBase;
using static NewLife.Remoting.ApiHttpClient;

namespace UtilsMQ
{
    /// <summary>
    /// 网上资料：报文的理论最大长度为 268435456 字节,也就是 256 MB,当前已使用1-2K
    /// </summary>
    public class MqttClientService
    {
        public static IMqttClient _mqttClient;
        public static string _clientId = System.Guid.NewGuid().ToString();
        public static string _topic;

        public MqttClientService()
        {

            //创建一个文件，保存clientid
            string path = UtilsFileHelper.getParnetDirectory() + "/Configs/";
            string fileName = "applicationId.conf";
            _clientId = UtilsFileHelper.readFile(path + fileName).ToString();
            if (string.IsNullOrEmpty(_clientId))
            {
                _clientId = System.Guid.NewGuid().ToString();
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                UtilsFileHelper.writeFile(_clientId, path + fileName);
            }
        }
        public void MqttClientStart()
        {
            Console.WriteLine("***连接服务端：" + ConstantMqtt.ip + ":" + ConstantMqtt.port);
            var optionsBuilder = new MqttClientOptionsBuilder()
                .WithTcpServer(ConstantMqtt.ip, ConstantMqtt.port) // 要访问的mqtt服务端的 ip 和 端口号
                .WithCredentials(ConstantMqtt.username, ConstantMqtt.password) // 要访问的mqtt服务端的用户名和密码
                .WithClientId(_clientId) // 设置客户端id,只能唯一，重复ID会强制掉线，注释掉的话，系统自已会生成随机id
                .WithCleanSession()
                .WithTlsOptions(new MqttClientTlsOptions
                {
                    UseTls = false  // 是否使用 tls加密
                });

            //var optionsBuilder = new MqttClientOptionsBuilder()
            //    .WithTcpServer("127.0.0.1", 1338) // 要访问的mqtt服务端的 ip 和 端口号
            //    .WithCredentials("mqiot", "?456123") // 要访问的mqtt服务端的用户名和密码
            //    .WithClientId(_clientId) // 设置客户端id
            //    .WithCleanSession()
            //    .WithTls(new MqttClientOptionsBuilderTlsParameters
            //    {
            //        UseTls = false  // 是否使用 tls加密
            //    });

            var clientOptions = optionsBuilder.Build();
            _mqttClient = new MqttFactory().CreateMqttClient();
            _mqttClient.ConnectedAsync += _mqttClient_ConnectedAsync; // 客户端连接成功事件
            _mqttClient.DisconnectedAsync += _mqttClient_DisconnectedAsync; // 客户端连接关闭事件
            _mqttClient.ApplicationMessageReceivedAsync += _mqttClient_ApplicationMessageReceivedAsync; // 收到消息事件
            _mqttClient.ConnectAsync(clientOptions);
            if (_mqttClient.IsConnected)
            {
                Console.WriteLine("mqtt连接成功");
            }
            // new UtilsFileHelper().log_WriteMessageAppend("mqttclient_error", " 22");

        }

        /// <summary>
        /// 客户端连接关闭事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task _mqttClient_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
        {
            // new UtilsFileHelper().log_WriteMessageAppend("mqttclient_error", " 333");
            Console.WriteLine($"客户端已断开与服务端的连接……");
            Thread.Sleep(3000);
            MqttClientStart();//重新连接
            return Task.CompletedTask;
        }

        /// <summary>
        /// 客户端连接成功事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        private Task _mqttClient_ConnectedAsync(MqttClientConnectedEventArgs arg)
        {
            Console.WriteLine($"客户端已连接服务端……topic:"+ _topic);

            // 订阅消息主题
            // MqttQualityOfServiceLevel: （QoS）:  0 最多一次，接收者不确认收到消息，并且消息不被发送者存储和重新发送提供与底层 TCP 协议相同的保证。
            // 1: 保证一条消息至少有一次会传递给接收方。发送方存储消息，直到它从接收方收到确认收到消息的数据包。一条消息可以多次发送或传递。
            // 2: 保证每条消息仅由预期的收件人接收一次。级别2是最安全和最慢的服务质量级别，保证由发送方和接收方之间的至少两个请求/响应（四次握手）。

            //QoS 0，最多交付一次。
            //QoS 1，至少交付一次。
            //QoS 2，只交付一次。
            _mqttClient.SubscribeAsync(_topic, MqttQualityOfServiceLevel.AtMostOnce);
            return Task.CompletedTask;
        }

        /// <summary>
        /// 收到消息事件
        /// </summary>
        /// <param name="arg"></param>
        /// <returns></returns>
        public virtual Task _mqttClient_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
        {
           // Console.WriteLine($"ApplicationMessageReceivedAsync：客户端ID=【{arg.ClientId}】接收到消息。Topic主题=【{arg.ApplicationMessage.Topic}】 消息=【{Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment)}】 qos等级=【{arg.ApplicationMessage.QualityOfServiceLevel}】");
            return Task.CompletedTask;
        }

        public bool Publish(string topic, string data)
        {
            try
            {
                if (_mqttClient.IsConnected == false)
                {
                    Console.WriteLine("RemoteMqttClient topic:" + topic + " mqtt 连接失败...");
                    return false;
                }
                var message = new MqttApplicationMessage
                {
                    Topic = topic,//ConstantMqtt.serverClientId "topic_02",
                    PayloadSegment = Encoding.Default.GetBytes(data),
                    QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
                    MessageExpiryInterval = 60,//,// 消息过期时间
                    //MessageExpiryInterval = 10,
                    Retain = false  // 服务端是否保留消息。true为保留，如果有新的订阅者连接，就会立马收到该消息。
                };
                _mqttClient.PublishAsync(message);
                Console.WriteLine("RemoteMqttClient topic:" + topic + " mqtt 通讯正常...");

                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            return false;
        }

        public bool PublishByte(string topic, Byte[] data)
        {
            try
            {
                if (_mqttClient.IsConnected == false)
                {
                    Console.WriteLine("RemoteMqttClient topic:" + topic + " mqtt 连接失败...");
                    return false;
                }
                var message = new MqttApplicationMessage
                {
                    Topic = topic,//ConstantMqtt.serverClientId "topic_02",
                    PayloadSegment = data,
                    QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
                    MessageExpiryInterval = 60,//,// 消息过期时间
                    //MessageExpiryInterval = 10,
                    Retain = false  // 服务端是否保留消息。true为保留，如果有新的订阅者连接，就会立马收到该消息。
                };
                _mqttClient.PublishAsync(message);
                Console.WriteLine("RemoteMqttClient topic:" + topic + " mqtt 通讯正常...");

                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            return false;
        }


        public bool PublishRetain(string topic, string data)
        {
            try
            {
                if (_mqttClient.IsConnected == false)
                {
                    Console.WriteLine("RemoteMqttClient topic:" + topic + " mqtt 连接失败...");
                    return false;
                }
                var message = new MqttApplicationMessage
                {
                    Topic = topic,//ConstantMqtt.serverClientId "topic_02",
                    PayloadSegment = Encoding.Default.GetBytes(data),
                    QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
                    MessageExpiryInterval = 600,//,// 消息过期时间
                    //MessageExpiryInterval = 10,
                    Retain = true  // 服务端是否保留消息。true为保留，如果有新的订阅者连接，就会立马收到该消息。
                };
                _mqttClient.PublishAsync(message);
                Console.WriteLine("RemoteMqttClient topic:" + topic + " mqtt 通讯正常...");

                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            return false;
        }
    }
}
